/*
    模拟停车场的运行（生产者、消费者模型）
    停车位    多入口    多出口
*/

#include <unistd.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <semaphore.h>
#include <string.h>

#define park_num 5
#define entry_num 2
#define out_num 2
#define car_num 10

int running = 1;

typedef struct
{
    int id;
}car;

typedef struct
{
    car* q_entry[park_num*2];   //等待入口队列
    int front;
    int rear;   //下一个数据的存储位置
    pthread_mutex_t mut;    //等待队列的锁
    sem_t size;     //队列的信号量
}queue;

typedef struct
{
    sem_t free_num;     //车位的信号量
    pthread_mutex_t mut;
    int spots[park_num];    //标记是否被占用
}parking;

queue queue_entry;
parking park;

void* entrance(void* avg);
void* out_thread(void* avg);

int main()
{
    //随机数种子
    srand(time(NULL));

    // 初始化结构体
    sem_init(&park.free_num, 0, park_num);
    memset(park.spots, 0, sizeof(park.spots));
    pthread_mutex_init(&park.mut, NULL);

    //初始化入口队列
    queue_entry.front = queue_entry.rear = 0;
    pthread_mutex_init(&queue_entry.mut, NULL);
    sem_init(&queue_entry.size, 0, 0);

    //模拟入口
    int entry[entry_num] = {0, 1};
    pthread_t entry_tid[entry_num];
    for(int i = 0; i < entry_num; i++)
    {
        pthread_create(&entry_tid[i], NULL, entrance, &entry[i]);
    }

    //模拟汽车随机到达入口
    for(int i = 0; i < car_num; i++)
    {
        car* car1 = (car*)malloc(sizeof(car));
        car1->id = rand()%100;      //随机生成id

        pthread_mutex_lock(&queue_entry.mut);
        //加入car1到队列中
        queue_entry.q_entry[queue_entry.rear] = car1;
        queue_entry.rear = (queue_entry.rear + 1)%(park_num*2);
        pthread_mutex_unlock(&queue_entry.mut);
        sem_post(&queue_entry.size);
        usleep(200000); // 增加延时，模拟车辆到达间隔 0.2 秒
    }

    //创建出口线程
    int exit[out_num] = {0, 1};
    pthread_t exit_tid[out_num];
    for(int i = 0; i < out_num; i++)
    {
        pthread_create(&exit_tid[i], NULL, out_thread, &exit[i]);
    }

    // 等待所有线程完成（这里我们不退出，所以用 sleep 模拟）
    sleep(5); // 给线程足够时间处理车辆

    // 通知线程退出
    running = 0;
    // 唤醒所有入口线程，防止它们阻塞在 sem_wait
    for (int i = 0; i < entry_num; i++) 
        sem_post(&queue_entry.size);

    // join 所有线程
    for(int i = 0; i < out_num; i++)
    {
        pthread_join(exit_tid[i], NULL);
    }
    for(int i = 0; i < entry_num; i++)
    {
        pthread_join(entry_tid[i], NULL);
    }

    // 销毁资源
    pthread_mutex_destroy(&queue_entry.mut);
    sem_destroy(&queue_entry.size);
    pthread_mutex_destroy(&park.mut);
    sem_destroy(&park.free_num);

    return 0;
}

// 入口线程执行
void* entrance(void* avg)
{
    int eid = *(int*)avg;
    while(running)
    {
        //等待不为0
        sem_wait(&queue_entry.size);
        // 如果running为0，则退出循环
        if (!running) break;
        // 此时收到 有车信号
        pthread_mutex_lock(&queue_entry.mut);
        // 还要判断有没有车
        if(queue_entry.rear == queue_entry.front)
        {
            pthread_mutex_unlock(&queue_entry.mut);
            continue;
        }
        //有车执行提车操作
        car* car1 = queue_entry.q_entry[queue_entry.front];
        queue_entry.front = (queue_entry.front+1)%(park_num*2);
        printf("%d 车辆到达入口%d\n", car1->id, eid);
        pthread_mutex_unlock(&queue_entry.mut);
        
        //找一个位置停车
        sem_wait(&park.free_num);
        pthread_mutex_lock(&park.mut);
        for(int i = 0; i < park_num; i++)
        {
            if(park.spots[i] == 0)
            {
                park.spots[i] = 1;
                printf("入口 %d: 车辆 %d 停入车位 %d\n", eid, car1->id, i);
                break;
            }
        }
        pthread_mutex_unlock(&park.mut);
        free(car1);
        usleep(100000); // 增加延时，模拟停车操作耗时 0.1 秒
    }
    return NULL;
}

//出口函数执行
void* out_thread(void* avg)
{
    int out_id = *(int*)avg;
    while(running)
    {   
        pthread_mutex_lock(&park.mut);
        for(int j = 0; j < park_num; j++)
        {
            if(park.spots[j] == 1)
            {
                printf("%d 车位上的汽车已从出口 %d 离开\n", j, out_id);
                park.spots[j] = 0;
                sem_post(&park.free_num);
                break;
            }
        }
        pthread_mutex_unlock(&park.mut);
        sleep(1); // 防止死循环占用CPU
    }
    return NULL;
}